package heap;

import lombok.NoArgsConstructor;
import lombok.SneakyThrows;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;

/**
 * https://www.bilibili.com/video/BV1xJ411n77R
 * 泛型
 * https://blog.csdn.net/f641385712/article/details/81713103
 * https://blog.csdn.net/f641385712/article/details/88789847
 * https://blog.csdn.net/f641385712/article/details/94174366
 * Java 的泛型是"假"的，Java 为了向下兼容，提出来的泛型只是一个语法糖，并不是像 c++ 那样是真的泛型
 * 虽然 java 中的泛型是“假”的，会有泛型擦除的操作，但是不可否认，泛型的引入让 Java 语言上升了一大步
 * <p>
 * 泛型就是参数化类型：
 * * 适用于多种数据类型执行相同的代码
 * * 泛型中的类型在使用时指定
 * * 泛型归根到底就是“模版”
 * 优点：使用泛型时，在实际使用之前类型就已经确定了，不需要强制类型转换。提高安全性。
 * * 泛型类型
 * * 泛型类
 * * 泛型接口
 * * 泛型方法
 * * 泛型的继承 extends/super
 * * 泛型通配符
 * <p>
 * 泛型是 Java 1.5 版本才引进的概念，在这之前是没有泛型的，但是，泛型代码能够很好地和之前版本的代码兼容。
 * 那是因为，泛型信息只存在于代码编译阶段，在进入 JVM 之前，与泛型相关的信息会被擦除掉，我们称之为类型擦除。
 */
public class T04_Generic {
    public static void main(String[] args) {
        demo1();
        demo2();
        demo3();
        demo4();
        demo5();
    }

    /**
     * 这里面就证明了虽然 List 声明为只能装 Integer 类型，但是却放进去了字符串类型，由此可见，泛型是“假”的，只存在于编译期
     */
    @SneakyThrows
    public static void demo1() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        // list.add("a"); // 这样直接添加肯定是不允许的

        // 下面通过 java 的反射，绕过泛型来给添加字符串
        Method add = list.getClass().getMethod("add", Object.class);
        add.invoke(list, "a");

        System.out.println(list); // [1, a] 输出没有没问题
        System.out.println(list.get(1)); // a
    }

    /**
     * 此处需要注意，虽然 1 放进去了。但是 get 出来的时候会报错，因为强转失败
     */
    @SneakyThrows
    public static void demo2() {
        Map<String, String> map = new HashMap<>();
        String key = "key";
        Integer val = new Integer(1); // 备注：此方法在 Java9 后标注为过期了，建议使用 valueOf，使用缓存来提高效率
        Method m = HashMap.class.getDeclaredMethod("put", new Class[]{Object.class, Object.class});
        m.invoke(map, key, val);

        System.out.println(map); // {key=1}
        // 但是下面的输出会报错
        System.out.println(map.get(key)); // java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    }

    @SneakyThrows
    public static void demo3() { // 泛型与反射
        // Class t04GenericClass = T04_Generic.class;
        // Constructor constructor = t04GenericClass.getConstructor();
        // Object o = constructor.newInstance();

        Class<T04_Generic> t04GenericClass = T04_Generic.class;
        Constructor<T04_Generic> constructor = t04GenericClass.getConstructor();
        T04_Generic t04_generic = constructor.newInstance(); // 不用强转
    }

    public static void demo4() { // 泛型擦除
        Arrays.stream(GoogleThree.class.getDeclaredFields()).forEach(field -> {
            // 说明泛型擦除，变为 Object 类型
            // 分为有限制类型擦除和直接擦除，区别在于是否有上下限
            System.out.println(field.getName() + "\t" + field.getType().getSimpleName());
        });

        Arrays.stream(GoogleTwo.class.getDeclaredMethods()).forEach(method -> {
            // 接口的实现类还会生成桥接方法
            System.out.println(method.getName() + "\t" + method.getReturnType().getSimpleName());
        });
    }

    public static void demo5() { // 泛型数组
        ArrayList[] arrayLists = new ArrayList[10];
        ArrayList<String>[] arrayLists1 = arrayLists;
        // ArrayList<String>[] arrayLists2 = new ArrayList<String>[10]; // 报错
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);

        arrayLists[0] = arrayList;
        System.out.println(arrayLists1[0].get(0)); // java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
    }

    // =================================================================================================================

    @NoArgsConstructor
    static class Product<T extends Object> {
        List<T> list;
        T[] array;

        public Product(Class<T> clz, int len) {
            array = (T[]) Array.newInstance(clz, len); // 泛型数组
        }

        public T getProduct(int i) {
            return list.get(i);
        }

        public <E> E getProduct(ArrayList<E> list, int i) { // 泛型方法
            return list.get(i);
        }
    }

    static class ProductOne<T> extends Product<T> {
    }

    static class ProductTwo extends Product<Object> { // 子类不是泛型，父类要明确
    }

    static void showProduct(Product<? extends Object> product) { // 类型通配符，上限，? 只能为 Object 的子类
        Object p = product.getProduct(0);
    }

    static void hideProduct(Product<? super Object> product) { // 类型通配符，下限，? 只能为 Object 的父类
        Object p = product.getProduct(0);
    }

    // ============================================== 泛型的使用 =========================================================

    interface Google<T> {
        T get();
    }

    static class GoogleOne<T> implements Google<T> {
        @Override
        public T get() {
            return null;
        }
    }

    static class GoogleTwo implements Google<Integer> {
        @Override
        public Integer get() {
            return null;
        }
    }

    static class GoogleThree<T, E> implements Google<T> {
        T key;
        E value;

        public GoogleThree(T key, E value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public T get() {
            return key;
        }
    }
}
